//<plumbing>
#include <tchar.h>
//</plumbing>
//<stdc>
#include <cstring>
#include <clocale>
#include <cstdio>
#include <cstdint>
#include <cinttypes>
//</stdc>
//<stdcxx>
#include <string>
#include <list>
#include <vector>
#include <iostream>
#include <thread>
#include <map>
#include <memory>
#include <sstream>
//</stdcxx>
using namespace std;

template <typename T>
class CMyPtr
{
private:
	typedef T *T_ptr_t;
	T_ptr_t m_p;

	CMyPtr(CMyPtr &c) {}

public:
	bool m_isArray;

	CMyPtr() : m_p(0), m_isArray(false) {}
	CMyPtr(T_ptr_t p) : m_p(p), m_isArray(false) {}
	CMyPtr(T &p) : m_p(&p), m_isArray(false) {}
	CMyPtr(CMyPtr&& other)
	{
		this->m_p = other.m_p;
		this->m_isArray = other.m_isArray;
		other.m_p = nullptr;
	}
	~CMyPtr() { Free(); }
	void Free()
	{
		if (m_p)
		{
			m_isArray ? delete[] m_p : delete m_p;
			m_p = 0;
		}
	}

	operator T_ptr_t() { return m_p; }
	T_ptr_t operator->() { return m_p; }
	T &operator*() { return *m_p; }

	bool operator!() { return m_p == 0; }
	operator bool() { return m_p != 0; }
	void operator=(T_ptr_t p)
	{
		Free();
		m_p = p;
	}
};

class Dummy
{
private:
	static int _rc;
	int m_i;

public:
	void Zzz() { printf("Dummy%d Zzz~~~\n", m_i); }
	Dummy()
	{
		m_i = _rc++;
		printf("Dummy%d\n", m_i);
	}
	~Dummy() { printf("~Dummy%d\n", m_i); }
};

int Dummy::_rc = 0;

typedef CMyPtr<Dummy> DummyPtr;
void ptrtest()
{
	auto check_valid = [](DummyPtr& ptr_) {
		if (ptr_) puts("ptr is valid");
		else puts("ptr is null");
	};
	auto check_null = [](DummyPtr& ptr_) {
		if (!ptr_) puts("ptr is null");
		else puts("ptr is valid");
	};
	DummyPtr ptr(new Dummy[3]);
	ptr.m_isArray = true;
	ptr->Zzz();

	ptr = new Dummy();
	ptr.m_isArray = false;
	ptr->Zzz();

	auto copy = move(ptr);

	check_valid(ptr);
	check_null(ptr);

	ptr.Free();
	check_valid(ptr);
	check_null(ptr);
}


struct Empty {/* empty */} empty;

typedef int64_t BIGINT;
typedef char *PSTR;
typedef const char *PCSTR;

enum class PetTypes
{
	Unknown = -1,
	Cat,
	Dog,
	Bird,
	Fish,
	Snake,
	Worm,
};

void threadfunction(int i)
{
	cout << __PRETTY_FUNCTION__ << ": " << i << endl;
}

class threadfunctor
{
public:
	void operator()(int i)
	{
		cout << __PRETTY_FUNCTION__ << ": " << i << endl;
	}
};

class threadworker
{
public:
	void start(int i)
	{
		cout << __PRETTY_FUNCTION__ << ": " << i << endl;
	}
};

void threadtests()
{
	auto thread1 = thread([](int i)
	{ cout << __PRETTY_FUNCTION__ << ": " << i << endl; }, 1111);
	thread1.join();

	auto thread2 = thread(move(threadfunction), 2222);
	thread2.join();

	threadfunctor functor;
	auto thread3 = thread(functor, 3333);
	thread3.join();

	threadworker worker;
	auto thread4 = thread(&threadworker::start, &worker, 4444);
	thread4.join();
}

#define PLSIZEWIDTH 18
#define PLSIZEOF(T) PrintSizeOf(#T, sizeof(T))
void PrintSizeOf(const char* name, uint32_t size)
{
    // Get prefix length: sizeof(%s)
    auto n = 8 + strlen(name) + 1;
    // Print the length into format string
    if (n < PLSIZEWIDTH)
        printf("sizeof(%s)%*c= %u\n", name, int(PLSIZEWIDTH - n), ' ', size);
    else
        printf("sizeof(%s) = %u\n", name, size);
}

unsigned long hash_djb2(char *str)
{
	unsigned long hash = 5381;
	unsigned int c;

	while (c = *(unsigned char *)str++)
		hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

	return hash;
}

unsigned long hash_sdbm(char *str)
{
	unsigned long hash = 0;
	unsigned int c;

	while (c = *(unsigned char *)str++)
		hash = c + (hash << 6) + (hash << 16) - hash;

	return hash;
}

typedef unsigned long hashfunc(char *);
#define HASHTEST(I, f) hashtest(I, f, #f)

void hashtest(int iterations, hashfunc func, const char* name)
{
	char sz[50];
	int collisions = 0;
	auto letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
					"01234567890~!@#$%^&*()_+{}|[].,<>";
	map<unsigned long, string> map;

	printf("Testing hash function %s\n", name);
	srand(time(0));
	auto vc = strlen(letters);
	for (int i = 0; i < iterations; i++)
	{
		for (int j = 0; j < sizeof(sz); j++)
		{
			sz[j] = letters[rand() % vc];
		}
		sz[sizeof(sz) - 1] = 0;
		auto key = func(sz);
		if (map.find(key) == map.end())
		{
			map[key] = sz;
		}
		else
		{
			collisions++;
			//printf("Found collision for key %lu:\n  %s\n  %s\n", key, sz, map[key].c_str());
		}
	}
	printf("Found %d collisions out of %d iterations\n", collisions, iterations);
}

int _tmain(int argc, _TCHAR **argv)
{
	auto i = 0, j = 0;

	//setlocale(LC_ALL, "");

	_putws(L"Commandline arguments:");
	for (int i = 0; i < argc; i++)
	{
		_tprintf_s(_T("%-2d: %s\n"), i, argv[i]);
	}
	_putws(L"");

	PLSIZEOF(_TCHAR);
	PLSIZEOF(short);
	PLSIZEOF(int);
	PLSIZEOF(long);
	PLSIZEOF(float);
	PLSIZEOF(double);
	PLSIZEOF(BIGINT);
	PLSIZEOF(Empty);
	PLSIZEOF(intptr_t);
	PLSIZEOF(void*);
	PLSIZEOF(long long);

	printf("%-6s=%p\n", "main", _tmain);
	printf("%-6s=%p\n", "argv", argv);
	printf("%-6s=%p\n", "&i", &i);
	printf("%-6s=%p\n", "&j", &j);

	auto favorite = PetTypes::Dog;
	cout << "enum value of Dog=" << (int)favorite << endl;

	printf("PRIx64=%s\n", PRIx64);
	_tprintf(_T("%" PRIx64 "\n"), 0x1234567890abcdef);

	threadtests();
	ptrtest();
	//HASHTEST(2000000, hash_djb2);
	//HASHTEST(2000000, hash_sdbm);
	return 0;
}

/* E.O.F */
